package enviornment;

import java.io.FileWriter;
import java.io.IOException;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;

import DataOutput.CsvOutput;
import au.com.bytecode.opencsv.CSVWriter;
import main.BuildModel;
import main.SmeLogging;
import repast.simphony.engine.schedule.ScheduledMethod;
import repast.simphony.essentials.RepastEssentials;
/**
 * Represents a flood which is implemented as agents with simple behaviours:
 * the inundation level changes with time passes by at various location.
 * @author Chunhui Li
 *
 */
public class Flood {
	private String ID;
	private double levelOfInundation;
	private double waterOnRoad;
	private Location location;
	private boolean becomeDangerous;
	private double[] buildingWater = new double [BuildModel.DATA_SIZE ];
	private double[] roadWater = new double [BuildModel.DATA_SIZE ];
	//private double [] all = new double [FLOOD_DATA_PERIOD * (DATA_SIZE-1)];
	private LinkedList<Double> all_flood;
	private LinkedList<Double> all_road;
	private int pointer;
	private double time;
	private boolean nightFlood;
	private Clock startFlood;
	private Clock finishFlood;
	private boolean flooded;
	//private CsvOutput csv;
//	private static Logger LOGGER_Flood = Logger.getLogger(Flood.class.getName());
	
	//private Map<Location, Integer> flood = new HashMap<Location, Integer>();
	/**
	 * The constructor of a flood agent.
	 * @param loc The location
	 */
	public Flood(Location loc){
		ID = "";
		levelOfInundation = 0.0;
		waterOnRoad=0.0;
		location = loc;
		pointer =0;
		all_flood = new LinkedList<Double>();
		all_road=new LinkedList<Double>();
		setBecomeDangerous(false);
		time=0.0;
		nightFlood=false;
		startFlood=new Clock();
		finishFlood =new Clock();
		flooded=false;
/*		
		csv = new CsvOutput(ID);
		String[] e ={"Time", "Water"};
		csv.writeEntry(e);
		try {
			csv.closeWriter();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}*/
	}
	public void setLogger() throws IOException{
		SmeLogging.setup("Flood_Log.txt");
	}
	public void checkNightFlood(){
		int j=1;
		int j_road=1;
		int x=0;
		int x_road=0;
		
		for(int i=0; i<buildingWater.length;i++){
			if(buildingWater[i]>0.0){
				setFlooded(true);
			}
		}
		if(flooded){
			for(int i=0;i<buildingWater.length-1; i++){
				if(buildingWater[i+1]==buildingWater[i] && buildingWater[i+1]==0.0){
					j++;
				}
				if(buildingWater[i+1]<buildingWater[i]&& buildingWater[i+1]==0.0){
					x=i+1;
				}
			}
			for(int i=0; i<roadWater.length-1;i++){
				if(roadWater[i+1]==roadWater[i] && roadWater[i+1]==0.0){
					j_road++;
				}
				if(roadWater[i+1]<roadWater[i]&& roadWater[i+1]==0.0){
					x_road=i+1;
				}
			}
			if(x==0||x_road==0){
				x= (int)(buildingWater.length+BuildModel.TICK_PER_HOUR*BuildModel.ADDED_HOURS);
			//	x=(int)(buildingWater.length+BuildModel.TICK_PER_HOUR*BuildModel.ADDED_HOURS);
			}
		}
		double tick_b=j+BuildModel.ADDED_HOURS*BuildModel.TICK_PER_HOUR;
		double tick_r=j_road+BuildModel.ADDED_HOURS*BuildModel.TICK_PER_HOUR;
		Clock c=new Clock();
		Clock c2=new Clock();
		if(tick_b<=tick_r){		
			c.convertTicks(tick_b);
		}else{
			c.convertTicks(tick_r);
		}		
		//Come back to check!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		double check= (int)(buildingWater.length+BuildModel.TICK_PER_HOUR*BuildModel.ADDED_HOURS);
		c2.convertTicks(check);
		setStartFlood(c);
		setFinishFlood(c2);
		System.out.println("*****Detect at" +c.toString()+ " flood starts");
		if(c.getHour()<BuildModel.START_WORK_HOUR ||c.getHour()>BuildModel.END_WORK_HOUR){
			setNightFlood(true);
		}else{
			setNightFlood(false);
		}
		
	}
	public void setFinishFlood(Clock c){
		finishFlood=c;
	}
	public Clock getFinishFlood(){
		return finishFlood;
	}
	public void setStartFlood(Clock c){
		startFlood=c;
	}
	public Clock getStartFlood(){
		return startFlood;
	}
	public void saveFloodData(){
		String name = this.ID;
		try {
			CSVWriter writer = new CSVWriter (new FileWriter(name+".csv"));
			String[] entries = {"Time", "Water"};			
			writer.writeNext(entries);	
			String[] data = new String[2];
			for(int i=0; i<all_flood.size(); i++){
				data[0] = String.valueOf(i);
				data[1]	= String.valueOf(all_flood.get(i));
				writer.writeNext(data);
			}
			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public String getID(){
		return ID;
	}
	public void setID(String s){
		this.ID= s;
	}
	public double getInundationLevel(){
		return levelOfInundation;
	}
	
	public String getFloodedCom(){
		return ID;
	}
	public void setFloodedCom(String s){
		ID =s ;
	}
	public Location getLocation(){
		return location;
	}
	public void setInundationLevel(double level){
		levelOfInundation = level;
	}
	public void setRoadWater(double level){
		waterOnRoad=level;
	}
	public double getRoadWater(){
		return waterOnRoad;
	}
	public void setBuildingData(int i, double x){
		if( i< buildingWater.length){
			buildingWater [i] = x;
	//		System.out.println(x+" BuildingWater");
		}
		else {
			System.out.println("error in setting the buildingWater"); // Rewrite later: create an exception
		}
		
	}
	public void setRoadData(int i, double x){
		if( i< roadWater.length){
			roadWater [i] = x;
//			System.out.println(x+" roadWater");
		}
		else {
			System.out.println("error in setting the roadWater"); // Rewrite later: create an exception
		}
		
	}
	
	/**
	 * Calculates the flood level at different ticks based on the input data
	 */
	/*
	public void preSettingFlood(){
		LinkedList<Double> level = new LinkedList<Double> ();
		all_flood.add(0.0);
		for (int j= 1; j<BuildModel.DATA_SIZE;j++){
			double delta = (buildingWater [j]- buildingWater [j-1])/(BuildModel.FLOOD_DATA_INTERVAL*BuildModel.TICK_PER_HOUR-1);
			level.add(buildingWater[j-1]);
			for (int i=1; i<=  BuildModel.FLOOD_DATA_INTERVAL*BuildModel.TICK_PER_HOUR; i++){
				level.add((double)Math.round((level.get(i-1)+delta)*100)/100);
			}
			level.remove(0);
			all_flood.addAll(level);
			level.clear();
		}
		//System.out.println("Print out the Array All_flood:" + all_flood.size());
	
		//to print out all values for debugging
		for (int x=0; x< all.size() ; x++){
			System.out.println(all.get(x));
		}
		
		
	}*/
	/*
	public void preSettingRoadWater(){
		LinkedList<Double> road = new LinkedList<Double> ();
		all_road.add(0.0);
		for (int j= 1; j<BuildModel.DATA_SIZE;j++){
			double delta = (roadWater [j]- roadWater [j-1])/(BuildModel.FLOOD_DATA_INTERVAL*BuildModel.TICK_PER_HOUR-1);
			road.add(roadWater[j-1]);
			System.out.println("Added new road data: "+delta);
			for (int i=1; i<=  BuildModel.FLOOD_DATA_INTERVAL*BuildModel.TICK_PER_HOUR; i++){
				double temp= (double)Math.round((road.get(i-1)+delta)*100)/100;
				road.add(temp);
				System.out.println("Added new road data: "+temp);
			}
			road.remove(0);
			all_road.addAll(road);
			road.clear();
		}
		System.out.println("Print out the Array All_road:" + all_flood.size());
		for (int x=0; x< all_road.size() ; x++){
			System.out.println(all_road.get(x));
		}
	}
	*/
	/**
	 * Updates the flood agent in every step.
	 */
	@ScheduledMethod (start = 1, interval = 1, priority =30)
	public void step(){
		
		time = RepastEssentials.GetTickCount();
		setBecomeDangerous(false);
		
		if(time<BuildModel.TICK_PER_HOUR*BuildModel.ADDED_HOURS){
			setInundationLevel(0.0);
			setRoadWater(0.0);
		}
		if (time<BuildModel.DATA_SIZE+BuildModel.TICK_PER_HOUR*BuildModel.ADDED_HOURS &&time>=BuildModel.TICK_PER_HOUR*BuildModel.ADDED_HOURS ){
			setInundationLevel(buildingWater[(int)(time-BuildModel.TICK_PER_HOUR*BuildModel.ADDED_HOURS)]);
			setRoadWater(roadWater[(int)(time-BuildModel.TICK_PER_HOUR*BuildModel.ADDED_HOURS)]);
		}
		else {
			setInundationLevel(0.0);
			setRoadWater(0.0);
		}
	
		String msg="Tick "+time+" water level on road: "+ waterOnRoad;
//		LOGGER_Flood.log(Level.INFO, msg);
	}
	public String getFloodID(){
		return ID;
	}
	/*
	public CsvOutput getCsvWriter(){
		return csv;
	}*/
	public void setFlooded(boolean b){
		flooded=b;
	}
	public boolean getFlooded(){
		return flooded;
	}
	public void setNightFlood(boolean b){
		nightFlood=b;
	}
	public boolean getNightFlood(){
		return nightFlood;
	}
	public boolean isBecomeDangerous() {
		return becomeDangerous;
	}

	public void setBecomeDangerous(boolean becomeDangerous) {
		this.becomeDangerous = becomeDangerous;
	}
	public double[] getBuildingWaterArray (){
		return buildingWater;
	}
	public double[] getRoadWaterArray (){
		return roadWater;
	}
	
	public String toString(){
		String s = location.toString() + " Water Level: " + levelOfInundation;
		return s;
	}

}
